Multi Part Download: Use Chunked Stream#4231
Multi Part Download: Use Chunked Stream#4231GarrettBeatty merged 11 commits intofeature/transfermanagerfrom
Conversation
1602121 to
5c59042
Compare
5c59042 to
0bf170a
Compare
There was a problem hiding this comment.
Pull request overview
This PR refactors the S3 multipart download buffering implementation to use a chunked stream approach instead of single large ArrayPool buffers. The change addresses potential issues with very large parts (>2GB) and Large Object Heap allocations while maintaining the same functional behavior.
Key Changes:
- Replaces
StreamPartBufferandBufferedDataSourcewithChunkedBufferStreamandChunkedPartDataSourcethat use multiple small 80KB ArrayPool chunks instead of single large buffers - Removes the obsolete
AddBufferoverload methods fromIPartBufferManagerinterface, consolidating onAddDataSource - Removes unused
configparameter fromBufferedMultipartStreamconstructor
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/src/Services/S3/Custom/Transfer/Internal/ChunkedBufferStream.cs | New core stream implementation that manages data across multiple 80KB ArrayPool chunks to avoid LOH and support parts >2GB |
| sdk/src/Services/S3/Custom/Transfer/Internal/ChunkedPartDataSource.cs | New IPartDataSource wrapper for ChunkedBufferStream with part number tracking and disposal management |
| sdk/src/Services/S3/Custom/Transfer/Internal/BufferedPartDataHandler.cs | Updated to create ChunkedPartDataSource instead of StreamPartBuffer for out-of-order parts |
| sdk/src/Services/S3/Custom/Transfer/Internal/BufferedMultipartStream.cs | Removed unused config parameter from constructor |
| sdk/src/Services/S3/Custom/Transfer/Internal/PartBufferManager.cs | Removed obsolete AddBuffer overload methods |
| sdk/src/Services/S3/Custom/Transfer/Internal/IPartBufferManager.cs | Removed obsolete AddBuffer method declarations from interface |
| sdk/src/Services/S3/Custom/Transfer/Internal/StreamPartBuffer.cs | Deleted - replaced by ChunkedBufferStream |
| sdk/src/Services/S3/Custom/Transfer/Internal/BufferedDataSource.cs | Deleted - replaced by ChunkedPartDataSource |
| sdk/test/Services/S3/UnitTests/Custom/StreamPartBufferTests.cs | Deleted - tests for removed class |
| sdk/test/Services/S3/UnitTests/Custom/BufferedDataSourceTests.cs | Deleted - tests for removed class |
| sdk/test/Services/S3/UnitTests/Custom/PartBufferManagerTests.cs | Updated all test cases to use ChunkedPartDataSource instead of StreamPartBuffer |
| sdk/test/Services/S3/UnitTests/Custom/BufferedPartDataHandlerTests.cs | Updated mocks and assertions to expect ChunkedPartDataSource instead of StreamPartBuffer |
| sdk/test/Services/S3/UnitTests/Custom/BufferedMultipartStreamTests.cs | Updated constructor calls to remove config parameter |
0bf170a to
7ec7d2f
Compare
7ec7d2f to
3ac6179
Compare
| #endregion | ||
|
|
||
| #region Logger | ||
| private readonly Logger _logger = Logger.GetLogger(typeof(PartBufferManager)); |
There was a problem hiding this comment.
fixing this to be consistent with the other classes
philasmar
left a comment
There was a problem hiding this comment.
The code looks fine but I am concerned about the performance drop. Could it be that the chunk size is too small and it's creating a lot of overhead? Could you do some testing to increase the chunk size to int.MaxValue (disregarding the LOH optimization) and see if we regain the old performance? If that's the case, maybe we need to find the sweet spot for the chunk size instead of trying to completely avoid the LOH?
update chunk sizes update logic update comments
talked on devex channel internally on why its okay to use 64KB default |
* Increase multipart upload default part size to 8MB (#4032) * Added PutObjectResponse to TransferUtilityUploadResponse mapping (#4045) * Add missing fields to Transfer Utility request objects (#4056) * Fix issue with HeadersCollection in ResponseMapperTests * Add Progress listeners for initiated, complete, and failed for simple upload (#4059) * Add mapping of CompletemultipartUploadResponse to TransferUtilityUploadResponse (#4060) * Add multipartupload lifecycle tracking (#4061) * fix silently failing test (#4073) * Add ContentLanguage to header collection of GetObjectResponse. (#4074) * ignore isset (#4082) * Add DownloadResponse mapping (#4075) * Add additional validation to UploadPartRequests in Transfer Utility (#4083) * Update Response mapping logic for PutObjectResponse and CompleteMultipartResponse (#4086) * create tuabortmultipartuploadrequest (#4093) * Remove AmazonWebServiceResponse as base class for transfer utility repsonse objects. (#4087) stack-info: PR: #4087, branch: GarrettBeatty/stacked/12 * Add GetObjectResponse to TransferUtilityOpenStreamResponse mapping. (#4076) stack-info: PR: #4076, branch: GarrettBeatty/stacked/9 * Fix Unit test (#4108) * Add UploadWithResponseAsync api (#4105) * Add DownloadInitiated, Failed and Completed events (#4079) * Populate TransferUtilityDownloadDirectoryResponse with total objects downloaded (#4109) * Added UploadWithResponse and UploadWithResponseAsync methods to ITransferUtility interface (#4143) * Update TransferUtilityConfig and BaseDownloadRequest to add multi part download config options (#4120) * Fix content language initialization (#4168) * Multi Part Download + OpenStreamWithResponseAsync (#4130) * DownloadWithResponse with multipartdownload (#4136) * Make AddBuffer non async (#4173) * add failure policy to download directory (#4151) * fix test (#4175) * Add progress tracking for multi part download to files (#4139) * Add DownloadDirectoryWithResponse (#4141) * Optimize part streaming (#4162) * Update initiated, complete and failed events to not fire in background (#4170) * Move MaxInMemoryParts to request object (#4163) * Add error message for multipart download when using Encryption Client (#4171) * add failure policy to upload directory (#4181) * Fix download directory concurrency issue and refactor (#4180) * Update code to acquire capacity before starting task (#4182) * use throttling for discoverpart (#4183) * refactor upload directory and fix concurrency bug (#4186) * DownloadDirectory Initiated, Failed and Completed Events (#4176) * update logging (#4188) * Create UploadDirectoryWithResponse api + progress tracking events + update docs and sync exception handling (#4187) * fix test (#4190) * fix integ test build (#4191) * fix build. (#4192) * remove InternalsVisibleTo for S3 integration tests (#4194) * Fix DownloadPartProgressEventCallback race condition (#4196) * fix http concurrency (#4218) * optimize task creation (#4219) * use max size semaphore (#4220) * fix retrying valid IO exceptions * update dev configs (#4201) * Doc updates (#4229) * refactor (#4233) * code refactor src (#4235) * queue fixes (#4228) * add case sensitivity check to download directory command * Content Language Bug Fix (#4224) * Multi Part Download: Use Chunked Stream (#4231) * cleanup (#4232) * async filestream * Revert "async filestream" This reverts commit 567d815. * make ChunkBufferSize public (#4263) * async filestream (#4264) * update docs (#4265) * update docs * fix typo * Fix doc build (#4268) * Fix doc build * Revert "Fix doc build" This reverts commit 341f50b. * fix doc build * Delete generator/.DevConfigs/f8a7b6c5-d4e3-2f1a-0b9c-8d7e6f5a4b3c.json * Update changelog for S3 UploadWithResponse methods * Improve ArrayPool chunk disposal (#4272) * dispose individual chunks * Add log --------- Co-authored-by: Afroz Mohammed <afrozam@amazon.com> Co-authored-by: Garrett Beatty <gcbeatty@amazon.com> * make filestream not set async options (#4280) * make maxinmemorypartsnullable (#4279) --------- Co-authored-by: Philippe El Asmar <53088140+philasmar@users.noreply.github.com> Co-authored-by: Phil Asmar <phil.asmar@gmail.com> Co-authored-by: Afroz Mohammed <afroz429@gmail.com> Co-authored-by: Afroz Mohammed <afrozam@amazon.com>
Description
This PR refactors the buffered multipart download data handling by replacing the single large ArrayPool buffer approach with a chunked buffer stream design. The key changes include:
ChunkedBufferStreamclass: A writable/readable stream that stores data across multiple small (64KB) ArrayPool buffers, avoiding Large Object Heap (LOH) allocationsChunkedPartDataSourceclass: AnIPartDataSourceimplementation that wrapsChunkedBufferStreamfor seamless integration with thePartBufferManagerBufferedDataSourceandStreamPartBufferclasses: These are replaced by the new chunked approachBufferedPartDataHandler: Modified to use the newChunkedBufferStreaminstead ofStreamPartBufferPartBufferManagerandIPartBufferManager: Simplified interface and implementation to work with the new data source patternMotivation and Context
The previous buffering implementation used single large ArrayPool buffers which had two issues:
The new chunked approach:
#3806
Testing
ChunkedBufferStream(878 lines) covering:ChunkedPartDataSource(602 lines) covering:BufferedPartDataHandlerTestsandPartBufferManagerTeststo work with the new implementationBufferedDataSourceandStreamPartBufferPerformance Testing
I did notice some slowdown from the older implementation
New
vs
Old
50TB Testing
I also tested with the 50TB file and didnt see any issues
Screenshots (if appropriate)
Ran using visual studio performance analyzer
before change
after change
no LOH allocations
Types of changes
Checklist
License